/* -*- c++ -*-
   convert_instrs.cc - LLVM-IR to GCC conversion - instructions.
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

/* Conversion code for LLVM IR instructions, except those related to
   functions. */

#include "llvmir.h"
#include "parser.h"

#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/GlobalValue.h"
#include "llvm/InlineAsm.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Instruction.h"
#include "llvm/Instructions.h"
#include "llvm/Constants.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/DebugLoc.h"
#include "llvm/Support/raw_os_ostream.h"

namespace llvmir
{

  /* 
   * Instruction conversions
   */

  /* Convert an inline asm instruction. */
  InstrRepr*
  Parser::convert_instr_asm(Context *context,
                            const llvm::Instruction* llvm_instr)
  {
    /*
     * Function calls.
     * --------
     * [| call void asm <asm-string> <constraints> ( <arg1>, .., <argn> ) |] 
     * -->
     * ASM_EXPR
     *   <asm-string>,
     *   ASM_CLOBBERS(< constraints >)
     *   ASM_OUTPUTS(),
     *   ASM_INPUTS([| arg1 |], .., [| argn |]),
     *
     * [| call <ty> asm <asm-string> <constraints> ( <arg1>, .., <argn> ) |] 
     * -->
     * VAR_DECL V
     * ASM_EXPR
     *   <asm-string>,
     *   ASM_CLOBBERS([| constraints |])
     *   ASM_OUTPUTS(V),
     *   ASM_INPUTS([| arg1 |], .., [| argn |]),
     *
     * Note: Uses LLVM to process the constraint string which may
     * lead to incompatibilities with GCC asm expressions.
     */
    gcc_assert(llvm_instr);
    FnContext* const fn_context = FnContext::from(context->curr_context);
    location_t instr_loc = 
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::CallInst* llvm_call = 
      static_cast<const llvm::CallInst*>(llvm_instr);
    const llvm::Type* llvm_type = llvm_call->getType();
    const unsigned num_args = llvm_call->getNumArgOperands(); 
    const llvm::Value* llvm_fnptr = llvm_call->getCalledValue();
    const llvm::InlineAsm* llvm_asm = 
      static_cast<const llvm::InlineAsm*>(llvm_fnptr);
    const llvm::InlineAsm::ConstraintInfoVector& llvm_cnstrt_vec =
      llvm_asm->ParseConstraints();
    ValueReprSeq arg_repr_seq;
    TypeRepr* type_repr = 0;
    InstrRepr* instr_repr = 0;
    std::string asm_string;
    tree var_tree = NULL_TREE;
    tree asm_tree = NULL_TREE;
    tree instr_tree = NULL_TREE;

    // Convert the type.
    type_repr = convert_type(context, llvm_type);
    gcc_assert(type_repr && type_repr->get_tree());

    // Create a variable if necessary.
    if (!llvm_type->isVoidTy())
      {
        const std::string& llvm_base_name = llvm_instr->getName();
        const std::string llvm_name = "asm." + llvm_base_name;
        var_tree = Tree::build_var_decl(instr_loc, 
                                        llvm_name,
                                        type_repr->get_tree(),
                                        true);

        Tree::declare_var(var_tree, 
                          fn_context->fn_repr->get_decln(),
                          fn_context->fn_repr->scope_tree);
      }

    // Convert the arguments.
    if (num_args)
      {
        arg_repr_seq.resize(num_args);

        for(unsigned itr = 0; itr < num_args; itr += 1)
          {
            arg_repr_seq[itr] = convert_value(context, 
                                              llvm_call->getArgOperand(itr));

            gcc_assert(arg_repr_seq[itr] && arg_repr_seq[itr]->get_tree());
          }
      }

    asm_tree = make_asm_tree(context, type_repr->get_tree(),
                             llvm_asm->getAsmString(), 
                             &llvm_cnstrt_vec,
                             &arg_repr_seq, 
                             var_tree);

    // Make a binding to the variable, if necessary.
    if (var_tree)
      instr_tree = fold_build2_loc(instr_loc,
                                   MODIFY_EXPR,
                                   type_repr->get_tree(),
                                   var_tree,
                                   asm_tree);
    else
      instr_tree = asm_tree;

    // Make the representation.
    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    fn_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /* Convert a, possibly conditional, branch instruction. */
  InstrRepr* 
  Parser::convert_instr_branch(Context *context,
                               const llvm::Instruction *llvm_instr)
  {
    /*
     * [| BR target |] 
     * --> 
     * GOTO_EXPR [| target |]
     *
     * [| BR cond, true-target, false-target]
     * -->
     * COND_EXPR [| cond |], 
     *           (GOTO_EXPR [| true-target |]), 
     *           (GOTO_EXPR [| false-target |])
     */
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::BranchInst* llvm_branch = 
      static_cast<const llvm::BranchInst*>(llvm_instr);
    const llvm::Value* llvm_cond = 0;
    const llvm::Value* llvm_tlabel = 0;
    const llvm::Value* llvm_flabel = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    tree target_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;

    // Get the LLVM values.
    if (llvm_branch->isConditional())
      {
        ValueRepr* cond_repr = 0;
        ValueRepr* flabel_repr = 0;
        ValueRepr* tlabel_repr = 0;
        tree tbranch_tree = NULL_TREE;
        tree fbranch_tree = NULL_TREE;

        llvm_cond = llvm_branch->getCondition();
        llvm_flabel = llvm_branch->getOperand(1);
        llvm_tlabel = llvm_branch->getOperand(2);
        
        // Convert the operands
        cond_repr = convert_value(context, llvm_cond);
        flabel_repr = convert_value(context, llvm_flabel);
        tlabel_repr = convert_value(context, llvm_tlabel);

        gcc_assert(cond_repr);
        gcc_assert(cond_repr->get_tree());
        gcc_assert(flabel_repr);
        gcc_assert(flabel_repr->get_tree());
        gcc_assert(tlabel_repr);
        gcc_assert(tlabel_repr->get_tree());
        
        // Form the target expressions
        fbranch_tree = fold_build1_loc(instr_loc,
                                       GOTO_EXPR, void_type_node,
                                       flabel_repr->get_tree());
        tbranch_tree = fold_build1_loc(instr_loc,
                                       GOTO_EXPR, void_type_node,
                                       tlabel_repr->get_tree());

        // Form the conditional branch instruction.
        instr_tree = fold_build3_loc(instr_loc,
                                     COND_EXPR, void_type_node,
                                     cond_repr->get_tree(),
                                     tbranch_tree,
                                     fbranch_tree);
      }
    else // Unconditional
      {
        ValueRepr* tlabel_repr = 0;

        llvm_tlabel = llvm_branch->getOperand(0);
        
        // Convert the operands
        tlabel_repr = convert_value(context, llvm_tlabel);

        gcc_assert(tlabel_repr);
        gcc_assert(tlabel_repr->get_tree());
        
        // Form the target expression.
        target_tree = tlabel_repr->get_tree();

        // Form the branch instruction.
        instr_tree = fold_build1_loc(instr_loc, 
                                     GOTO_EXPR, void_type_node,
                                     target_tree);
      }

    instr_repr = new TerminalInstrRepr(llvm_branch, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert an indirect branch instruction. */
  InstrRepr*
  Parser::convert_instr_indirectbr(Context *context,
                                   const llvm::Instruction *llvm_instr)
  {
    /*
     * [| INDIRECTBR ptr, label1, ..., labeln |] 
     * --> 
     * GOTO_EXPR (INDIRECT_REF [| ptr |])
     */
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::IndirectBrInst* llvm_indirectbr = 
      static_cast<const llvm::IndirectBrInst*>(llvm_instr);
    const llvm::Value* llvm_ptr = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    tree target_tree = NULL_TREE;
    ValueRepr* ptr_repr = 0;

    curr_context->src_loc = instr_loc;

    // Get the LLVM value.
    llvm_ptr = llvm_indirectbr->getOperand(0);
        
    // Convert the operands
    ptr_repr = convert_value(context, llvm_ptr);

    gcc_assert(ptr_repr);
    gcc_assert(ptr_repr->get_tree());
        
    // Form the target expression.
    target_tree = build_fold_indirect_ref_loc(instr_loc, ptr_repr->get_tree());

    // Form the branch instruction.
    instr_tree = fold_build1_loc(instr_loc, 
                                 GOTO_EXPR, void_type_node,
                                 target_tree);

    instr_repr = new TerminalInstrRepr(llvm_indirectbr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert a switch instruction. */
  InstrRepr*
  Parser::convert_instr_switch(Context *context,
                               const llvm::Instruction *llvm_instr)
  {
    /*
     * [| SWITCH value, default_target, (vali, targeti)* |] 
     * --> 
     * SELECT_EXPR [| value |],
     *   CASE_LABEL_EXPR NULL_TREE
     *     GOTO_EXPR [| default_target |]
     *   (CASE_LABEL_EXPR [| vali |]
     *      GOTO_EXPR [| targeti |])*
     *
     */
    FnContext* const curr_context = FnContext::from(context->curr_context);
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::SwitchInst* llvm_switch = 
      static_cast<const llvm::SwitchInst*>(llvm_instr);
    const llvm::Value* llvm_cond = 0;
    const llvm::Type* llvm_cond_type = 0;
    const llvm::Value* llvm_default = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    tree case_tree = NULL_TREE;
    tree label_tree = NULL_TREE;
    tree label_id_tree = NULL_TREE;
    tree body_tree = NULL_TREE;
    tree cond_type_tree = NULL_TREE;
    tree switch_body = NULL_TREE;
    std::string llvm_name;
    ValueRepr* cond_repr = 0;
    TypeRepr* cond_type_repr = 0;
    ValueRepr* default_repr = 0;
    unsigned num_cases = llvm_switch->getNumCases();

    /*
      The generated SWITCH_EXPR has to get past
      gimplify.c:gimplify_switch_expr.
      
      A SWITCH_EXPR e has a condition on which to switch,
      SWITCH_COND(e), a body, SWITCH_BODY(e), which is a list of
      statments, which may or may not contain CASE_LABEL_EXPRS, and a
      vector, SWITCH_LABELS(e), which is supposed to be a vector of
      the CASE_LABEL_EXPRS recording the value and target for each
      switch case. 

      Function gimplify_switch_expr turns a SWITCH_EXPR into a gimple
      construct. It has the following gotchas:

      - If both SWITCH_BODY and SWITCH_LABELS are given,
      (SWITCH_BODY(e) != NULL_TREE & SWITCH_LABELS(e) != NULL_TREE)
      gimplify_switch_expr will assert.

      - If SWITCH_LABELS(e) is given (and SWITCH_BODY(e) == NULL),
      gimplify_switch_expr will do nothing, effectively ignoring the
      whole SWITCH_EXPR.

      The documentation for gimplify_switch_expr seems to suggest
      that the default CASE_LABEL_EXPR must be the last
      CASE_LABEL_EXPR in SWITCH_BODY. This does not appear to be true
      (in fact the gimple switch construct appears to put its default
      case as the front of the list of cases).

      The SWITCH_EXPR for llvm instruction [| SWITCH value,
      default_target, (val_i, target_i)* |] is formed as:

      SWITCH_EXPR 
      SWITCH_COND = [| value |]
      SWITCH_BODY =
      {
      ...
      CASE_LABEL_EXPR [| val_i |], undef, target_i':
      goto [| target |]
      ...
      CASE_LABEL_EXPR undef, undef, default':
      goto default
      }
      SWITCH_LABELS = NULL_TREE
         
      where target_i' and default' are generated labels.
    */

    curr_context->src_loc = instr_loc;

    // Get the LLVM values.
    llvm_cond = llvm_switch->getCondition();
    gcc_assert(llvm_cond);
    llvm_cond_type = llvm_cond->getType();
    llvm_default = llvm_switch->getDefaultDest();
        
    // Convert the conditional and default values.
    cond_repr = convert_value(context, llvm_cond);
    gcc_assert(cond_repr);
    gcc_assert(cond_repr->get_tree());

    cond_type_repr = convert_type(context, llvm_cond_type);
    cond_type_tree = cond_type_repr->get_tree();
    gcc_assert(cond_type_repr);
    gcc_assert(cond_type_repr->get_tree());

    default_repr = convert_value(context, llvm_default);
    gcc_assert(default_repr);
    gcc_assert(default_repr->get_tree());
        
    // Form the list of case expressions. 
    // The case at index 0 (the default case).
    label_id_tree = Tree::make_name_tree("case_label", true);
    label_tree = 
      Tree::declare_label_tree(instr_loc, label_id_tree,
                               curr_context->fn_repr->get_decln(),
                               true);
    case_tree = Tree::build_case_label(NULL_TREE,
                                       NULL_TREE,
                                       label_tree);
    append_to_statement_list(case_tree, &switch_body);
      
    body_tree = fold_build1_loc(instr_loc,
                                GOTO_EXPR, void_type_node,
                                default_repr->get_tree());
    append_to_statement_list(body_tree, &switch_body);

    // The conditional cases start at index 1.
    for (unsigned idx = 1; idx < num_cases; idx += 1)
      {
        ValueRepr* value_repr = 0;
        ValueRepr* target_repr = 0;
        const llvm::ConstantInt *llvm_case_value = 
          llvm_switch->getCaseValue(idx);
        const llvm::BasicBlock *llvm_case_target = 
          llvm_switch->getSuccessor(idx);

        label_tree = NULL_TREE;
        label_id_tree = NULL_TREE;
        case_tree = NULL_TREE;
        body_tree = NULL_TREE;

        value_repr = convert_constant_int(context,
                                          llvm_case_value);
        gcc_assert(value_repr);
        gcc_assert(value_repr->get_tree());

        target_repr = convert_value(context, 
                                    llvm_case_target);
        gcc_assert(target_repr);
        gcc_assert(target_repr->get_tree());

        // Make the trees.
        label_id_tree = Tree::make_name_tree("case_label", true);
        label_tree = 
          Tree::declare_label_tree(instr_loc, label_id_tree,
                                   curr_context->fn_repr->get_decln(),
                                   true);

        case_tree = Tree::build_case_label(value_repr->get_tree(),
                                           NULL_TREE,
                                           label_tree);
        append_to_statement_list(case_tree, &switch_body);

        gcc_assert(CASE_LOW(case_tree) == value_repr->get_tree());
        gcc_assert(CASE_HIGH(case_tree) == NULL_TREE);
        gcc_assert(CASE_LABEL(case_tree) == label_tree);
      
        body_tree = fold_build1_loc(instr_loc,
                                    GOTO_EXPR, void_type_node,
                                    target_repr->get_tree());
        append_to_statement_list(body_tree, &switch_body);
      }
    // Form the switch instruction.
    instr_tree = fold_build3_loc(instr_loc, 
                                 SWITCH_EXPR, cond_type_tree,
                                 cond_repr->get_tree(),
                                 switch_body,
                                 NULL_TREE);

    gcc_assert(SWITCH_COND(instr_tree) == cond_repr->get_tree());
    gcc_assert(SWITCH_BODY(instr_tree) == switch_body);
    gcc_assert(SWITCH_LABELS(instr_tree) == NULL_TREE);

    instr_repr = new TerminalInstrRepr(llvm_switch, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert an unwind instruction. */
  InstrRepr*
  Parser::convert_instr_unwind(Context *context,
                               const llvm::Instruction *llvm_instr)
  {
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));

    /*
     * [| unwind |] 
     * --> 
     * Not implemented.
     */
    warning(instr_loc, "unwind instruction is not implemented.");
    gcc_unreachable();
  }

  /* Convert an unreachable instruction. */
  InstrRepr*
  Parser::convert_instr_unreachable(Context *context,
                                    const llvm::Instruction *llvm_instr)
  {
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));

    /*
     * [| unreachable |] 
     * --> 
     * Always fails.
     */
    error_at(instr_loc, "unreachable instruction found.");
    gcc_unreachable();
  }

  /* Convert a binary operation instruction */
  InstrRepr*
  Parser::convert_instr_binop_arith(Context *context,
                                    const llvm::Instruction *llvm_instr)
  {
    /*
     * Simple arithmetic:
     * --------
     * [| Add expr1, expr2 |] --> PLUS_EXPR [| expr1 |], [| expr2 |]
     * [| Sub expr1, expr2 |] --> MINUS_EXPR [| expr1 |], [| expr2 |]
     * [| Mul expr1, expr2 |] --> MULT_EXPR [| expr1 |], [| expr2 |]
     * 
     * Division and remainder:
     * --------
     * [| SDiv expr1, expr2 |] --> ROUND_DIV_EXPR [| expr1 |], [| expr2 |]
     * [| UDiv expr1, expr2 |] --> ROUND_DIV_EXPR [| expr1 |], [| expr2 |]
     * [| SRem expr1, expr2 |] --> ROUND_MOD_EXPR [| expr1 |], [| expr2 |]
     * [| URem expr1, expr2 |] --> ROUND_MOD_EXPR [| expr1 |], [| expr2 |]
     * 
     * [| FDiv expr1, expr2 |] --> RDIV_EXPR [| expr1 |], [| expr2 |]
     * [| FRem expr1, expr2 |] --> ROUND_MOD_EXPR [| expr1 |], [| expr2 |]
     * 
     * Bitwise operations:
     * --------
     * [| And expr1, expr2 |] --> BIT_AND_EXPR [| expr1 |], [| expr2 |]
     * [| Or expr1, expr2 |] --> BIT_IOR_EXPR [| expr1 |], [| expr2 |]
     * [| Xor expr1, expr2 |] --> BIT_XOR_EXPR [| expr1 |], [| expr2 |]
     * 
     * [| Shl expr1, expr2 |] --> LSHIFT_EXPR [| expr1 |], [| expr2 |]
     * [| Shr expr1, expr2 |] --> RSHIFT_EXPR [| expr1 |], [| expr2 |]
     * [| AShr expr1, expr2 |] --> RSHIFT_EXPR [| expr1 |], [| expr2 |]
     * 
     */
    assert(llvm_instr);
    const llvm::BinaryOperator* llvm_binop =
      static_cast<const llvm::BinaryOperator*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_binop->getDebugLoc()));
    const llvm::Type* llvm_type = llvm_binop->getType();
    const llvm::Value* llvm_expr1 = llvm_binop->getOperand(0);
    const llvm::Value* llvm_expr2 = llvm_binop->getOperand(1);
    tree_code expr_opcode = ERROR_MARK;
    tree instr_tree = NULL_TREE;
    tree type_tree = NULL_TREE;
    TypeRepr* type_repr = 0;
    ValueRepr* expr1_repr = 0;
    ValueRepr* expr2_repr = 0;
    tree expr1_tree = NULL_TREE;
    tree expr2_tree = NULL_TREE;
    InstrRepr* instr_repr = 0;

    curr_context->src_loc = instr_loc;

    // Convert the operands.
    expr1_repr = convert_value(context, llvm_expr1);
    expr2_repr = convert_value(context, llvm_expr2);

    gcc_assert(expr1_repr);
    expr1_tree = expr1_repr->get_tree();

    gcc_assert(expr2_repr);
    expr2_tree = expr2_repr->get_tree();

    // Choose the new opcode
    switch(llvm_binop->getOpcode())
      {
        // Arithmetic
      case llvm::Instruction::Add:
      case llvm::Instruction::FAdd:
        expr_opcode = PLUS_EXPR;
        break;
      case llvm::Instruction::Sub:
      case llvm::Instruction::FSub:
        expr_opcode = MINUS_EXPR;
        break;
      case llvm::Instruction::Mul:
      case llvm::Instruction::FMul:
        expr_opcode = MULT_EXPR;
        break;

      case llvm::Instruction::UDiv:
      case llvm::Instruction::SDiv:
        expr_opcode = TRUNC_DIV_EXPR;
        break;

      case llvm::Instruction::URem:
      case llvm::Instruction::SRem:
        expr_opcode = TRUNC_MOD_EXPR;
        break;

      case llvm::Instruction::FDiv:
        expr_opcode = TRUNC_DIV_EXPR;
        break;
      case llvm::Instruction::FRem:
        expr_opcode = TRUNC_MOD_EXPR;
        break;

        // Binary operations
      case llvm::Instruction::Shl:
        expr_opcode = LSHIFT_EXPR;
        break;
      case llvm::Instruction::LShr:
      case llvm::Instruction::AShr:
        expr_opcode = RSHIFT_EXPR;
        break;
      case llvm::Instruction::And:
        expr_opcode = BIT_AND_EXPR;
        break;
      case llvm::Instruction::Or:
        expr_opcode = BIT_IOR_EXPR;
        break;
      case llvm::Instruction::Xor:
        expr_opcode = BIT_XOR_EXPR;
        break;
      default:
        // Unknown Instruction
        return 0;
      }

    // Form the new instruction.
    if (llvm_type->isVectorTy())
      {
        const llvm::VectorType* llvm_vec_type = 
          static_cast<const llvm::VectorType*>(llvm_type);
        const llvm::Type* llvm_base_type =
          llvm_vec_type->getElementType();
        const unsigned num_elems = llvm_vec_type->getNumElements();
        Tree::VecMapper* mapper = 0;

        type_repr = convert_type(context, llvm_base_type);
        gcc_assert(type_repr && type_repr->get_tree());
        type_tree = type_repr->get_tree();

        mapper = new Tree::VecMapBinOp(instr_loc, 
                                       expr_opcode,
                                       type_tree,
                                       expr1_tree,
                                       expr2_tree);

        instr_tree = make_mapped_vector(type_tree, num_elems,
                                        mapper);

        delete(mapper); mapper = 0;
      }
    else
      {
        type_repr = convert_type(context, llvm_type);
        gcc_assert(type_repr && type_repr->get_tree());
        type_tree = type_repr->get_tree();

        instr_tree = fold_build2_loc(instr_loc,
                                     expr_opcode,
                                     type_tree,
                                     expr1_tree, 
                                     expr2_tree);
      }

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Build an expression to cast an expression from one bit-width to
     another. */
  tree make_int_cast(tree expr, tree src_type_tree, unsigned dst_size)
  {
    tree cast_tree = NULL_TREE;
    tree_code cast_opcode = CONVERT_EXPR;
    tree dst_type_tree = NULL_TREE;

    dst_type_tree = copy_node(src_type_tree);
    TYPE_PRECISION(dst_type_tree) = dst_size;

    cast_tree = fold_build1(cast_opcode, dst_type_tree, expr);

    return cast_tree;
  }

  /* Vector operation mapper to type convert the result of a
     mapper. */
  class VecMapConvert : public Tree::VecMapper
  {
  public:
    // Fields
    location_t loc;
    tree_code opcode;
    tree dst_type;
    VecMapper* mapper;

    // Constructor
    explicit 
    VecMapConvert(location_t l, tree_code op, tree ty, VecMapper* m)
      : loc(l), opcode(op), dst_type(ty), mapper(m)
    { /* skip */ }

    // Destructor
    ~VecMapConvert()
    {
      delete(mapper); mapper = 0;
    }

    tree build(const unsigned idx)
    {
      tree expr_tree = mapper->build(idx);
      tree element_tree = NULL_TREE;

      element_tree = fold_build1_loc(loc, opcode, dst_type, expr_tree);
      return element_tree;
    }
  };

  /* Convert a cast instruction */
  InstrRepr* 
  Parser::convert_instr_cast(Context *context,
                             const llvm::Instruction *llvm_instr)
  {
    /*
     * Simple type conversion:
     * --------
     * [| <cast-instr> src_type expr 'to' dst_type |]
     * -->
     * (CONVERT_EXPR [| expr |]): [| dst_type |]
     *
     * Pointer to integer
     * --------
     * [| PtrToInt src_type expr 'to' dst_type |]
     * -->
     * (CONVERT_EXPR expr1): [| dst_type |]
     * where
     *   expr1
     *     = [| expr |], if bitwidth(src_type) == bitwidth(dst_type)
     *     = (CONVERT_EXPR [| expr |]): new_src_type, otherwise
     *   new_src_type = dst_type with bitwidth(src_type)
     *
     * Integer to pointer
     * --------
     * [| IntToPtr src_type expr 'to' dst_type |]
     * -->
     * CONVERT_EXPR (expr1: [| dst_type |])
     * where
     *   expr1
     *     = [| expr |], if bitwidth(src_type) == bitwidth(dst_type)
     *     = (CONVERT_EXPR [| expr |]): new_src_type, otherwise
     *   new_src_type = src_type with bitwidth(dst_type)
     *
     */
    assert(llvm_instr);
    const llvm::CastInst* llvm_cast =
      static_cast<const llvm::CastInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_cast->getDebugLoc()));
    const llvm::Type* llvm_src_type = llvm_cast->getSrcTy();
    const llvm::Type* llvm_dst_type = llvm_cast->getDestTy();
    const llvm::Value* llvm_expr = llvm_cast->getOperand(0);
    tree_code expr_opcode = ERROR_MARK;
    tree instr_tree = NULL_TREE;
    tree expr_tree = NULL_TREE;
    TypeRepr* src_type_repr = 0;
    TypeRepr* dst_type_repr = 0;
    tree src_type_tree = NULL_TREE;
    tree dst_type_tree = NULL_TREE;
    ValueRepr* src_expr_repr = 0;
    tree src_expr_tree = NULL_TREE;
    InstrRepr* instr_repr = 0;

    curr_context->src_loc = instr_loc;

    // Convert the type and operands.
    src_type_repr = convert_type(context, llvm_src_type);
    src_type_tree = src_type_repr->get_tree();

    dst_type_repr = convert_type(context, llvm_dst_type);
    dst_type_tree = dst_type_repr->get_tree();

    src_expr_repr = convert_value(context, llvm_expr);
    gcc_assert(src_expr_repr);
    src_expr_tree = src_expr_repr->get_tree();
    expr_tree = src_expr_tree;

    gcc_assert(dst_type_tree);
    gcc_assert(src_type_tree);

    // Choose the new opcode
    switch(llvm_cast->getOpcode())
      {
        // Type conversion
      case llvm::Instruction::Trunc:
      case llvm::Instruction::ZExt:
      case llvm::Instruction::SExt:
        expr_opcode = CONVERT_EXPR;
        break;

      case llvm::Instruction::FPToUI:
      case llvm::Instruction::FPToSI:
        expr_opcode = FIX_TRUNC_EXPR;
        break;

      case llvm::Instruction::UIToFP:
      case llvm::Instruction::SIToFP:
        expr_opcode = FLOAT_EXPR;
        break;
        
      case llvm::Instruction::FPTrunc:
      case llvm::Instruction::FPExt:
        expr_opcode = FLOAT_EXPR;
        break;

      case llvm::Instruction::PtrToInt:
        {
          expr_opcode = CONVERT_EXPR;

          if (!llvm_dst_type->isVectorTy())
            {
              // Convert the pointer src to an integer isrc of the
              // same size, then specify a conversion from isrc to the
              // destination dst with the required size.
              if (TYPE_PRECISION(dst_type_tree) != TYPE_PRECISION(src_type_tree))
                expr_tree = make_int_cast(src_expr_tree,
                                          dst_type_tree,
                                          TYPE_PRECISION(src_type_tree));
            }
          break;
        }
      case llvm::Instruction::IntToPtr:
        {
          expr_opcode = CONVERT_EXPR;

          if (!llvm_dst_type->isVectorTy())
            {
              // Convert the integer src to an integer isrc of the
              // same size as the pointer dst, then specify a
              // conversion from isrc to the destination pointer.
              if (TYPE_PRECISION(dst_type_tree) != TYPE_PRECISION(src_type_tree))
                expr_tree = make_int_cast(src_expr_tree,
                                          src_type_tree,
                                          TYPE_PRECISION(dst_type_tree));
            }
          break;
        }

      case llvm::Instruction::BitCast:
        expr_opcode = VIEW_CONVERT_EXPR;
        break;

      default:
        // Unknown Instruction
        return 0;
      }

    // Form the new instruction.
    if (llvm_dst_type->isVectorTy())
      {
        // Vectors
        gcc_assert(llvm_src_type->isVectorTy());

        const llvm::VectorType* llvm_vecdst_type = 
          static_cast<const llvm::VectorType*>(llvm_dst_type);
        const unsigned num_elems = llvm_vecdst_type->getNumElements();
        const llvm::VectorType* llvm_vecsrc_type = 
          static_cast<const llvm::VectorType*>(llvm_src_type);
        TypeRepr* dst_base_type_repr = 0;
        tree dst_base_type = NULL_TREE;
        TypeRepr* src_base_type_repr = 0;
        tree src_base_type = NULL_TREE;
        Tree::VecMapper* mapper = 0;

        dst_base_type_repr = convert_type(context,
                                          llvm_vecdst_type->getElementType());
        gcc_assert(dst_base_type_repr 
                   && dst_base_type_repr->get_tree());
        dst_base_type = dst_base_type_repr->get_tree();

        src_base_type_repr = convert_type(context,
                                          llvm_vecsrc_type->getElementType());
        gcc_assert(src_base_type_repr 
                   && src_base_type_repr->get_tree());
        src_base_type = src_base_type_repr->get_tree();

        // Check for special cases.
        switch(llvm_cast->getOpcode())
          {
          case llvm::Instruction::PtrToInt:
            {
              // Convert the pointer src to an integer isrc of the
              // same size, then specify a conversion from isrc to the
              // destination dst with the required size.
              if (TYPE_PRECISION(dst_type_tree) == TYPE_PRECISION(src_type_tree))
                {
                  mapper = new Tree::VecMapUnOp(instr_loc, 
                                                CONVERT_EXPR,
                                                dst_base_type,
                                                src_expr_tree);
                }
              else
                {
                  Tree::VecMapUnOp *cast_mapper = 0;
                  tree tmp_src_type = copy_node(dst_base_type);
                  TYPE_PRECISION(tmp_src_type) = TYPE_PRECISION(src_base_type);
                  cast_mapper = new Tree::VecMapUnOp(instr_loc, 
                                                     CONVERT_EXPR,
                                                     tmp_src_type,
                                                     src_expr_tree);
                  mapper = new VecMapConvert(instr_loc,
                                             CONVERT_EXPR,
                                             dst_base_type,
                                             cast_mapper);
                }

              break;
            }
          case llvm::Instruction::IntToPtr:            
            {
              // Convert the integer src to an integer isrc of the
              // same size as the pointer dst, then specify a
              // conversion from isrc to the destination pointer.
              if (TYPE_PRECISION(dst_base_type) == TYPE_PRECISION(src_base_type))
                {
                  mapper = new Tree::VecMapUnOp(instr_loc, 
                                                CONVERT_EXPR,
                                                dst_base_type,
                                                src_expr_tree);
                }
              else
                {
                  Tree::VecMapUnOp *cast_mapper = 0;
                  tree tmp_src_type = copy_node(src_base_type);
                  TYPE_PRECISION(tmp_src_type) = TYPE_PRECISION(dst_base_type);
                  cast_mapper = new Tree::VecMapUnOp(instr_loc, 
                                                     CONVERT_EXPR,
                                                     tmp_src_type,
                                                     src_expr_tree);
                  mapper = new VecMapConvert(instr_loc,
                                             CONVERT_EXPR,
                                             dst_base_type,
                                             cast_mapper);
                }
              break;
            }
          default:
            {
              mapper = new Tree::VecMapUnOp(instr_loc, 
                                            CONVERT_EXPR,
                                            dst_base_type,
                                            src_expr_tree);
              break;
            }
          }

        // Make the instrcuction
        gcc_assert(mapper);
        instr_tree = Tree::make_mapped_vector(dst_base_type,
                                              num_elems,
                                              mapper);
        delete(mapper); mapper = 0;
      }
    else
      {
        // Scalars
        instr_tree = fold_build1_loc(instr_loc,
                                     expr_opcode,
                                     dst_type_tree,
                                     expr_tree);
      }

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /*
   * Memory access instructions
   */

  /* Convert an alloca instruction */
  InstrRepr*
  Parser::convert_instr_alloca(Context *context,
                               const llvm::Instruction *llvm_instr)
  {
    /*
     * Reserve stack space
     * --------
     * [| Alloca <elem_type>, type <num_elems>, align <alignment> |]
     * -->
     * if num_elems == 1:
     *   ADDR_EXPR (VAR_DECL: [| elem_type |]), 
     * if num_elems > 1:
     *   ADDR_EXPR (VAR_DECL: (ARRAY_TYPE [| elem_type |] * num_elems))
     *
     * Note:
     *  The VAR_DECL is anonymous.
     *  The alignment is applied to the VAR_DECL.
     *
     */
    gcc_assert(llvm_instr);
    gcc_assert(context && context->curr_context);
    const llvm::AllocaInst* llvm_alloca =
      static_cast<const llvm::AllocaInst*>(llvm_instr);
    FnContext* const curr_context = FnContext::from(context->curr_context);
    location_t instr_loc =
      convert_location(context, &(llvm_alloca->getDebugLoc()));
    const llvm::Type* llvm_type = llvm_alloca->getType();
    const llvm::Type* llvm_elem_type = llvm_alloca->getAllocatedType();
    const llvm::Value* llvm_num_elems = llvm_alloca->getArraySize();
    const unsigned llvm_alignment = llvm_alloca->getAlignment();
    tree elem_type = NULL_TREE;
    tree num_elems_tree = NULL_TREE;
    tree array_type = NULL_TREE;
    tree var_tree = NULL_TREE;
    TypeRepr* expr_type_repr = 0;
    TypeRepr* elem_type_repr = 0;
    tree expr_type = NULL_TREE;
    tree expr_tree = NULL_TREE;
    bool single_elem = false;
    InstrRepr* instr_repr = 0;
    
    // Test for an allocation of 1 element.
    if (!llvm_alloca->isArrayAllocation())
      single_elem = true;
    
    // Convert the llvm elements
    expr_type_repr = convert_type(context, llvm_type);
    expr_type = expr_type_repr->get_tree();

    elem_type_repr = convert_type(context, llvm_elem_type);
    elem_type = elem_type_repr->get_tree();

    // Make the array type.
    if (single_elem)
      array_type = elem_type;
    else
      {
        ValueRepr* num_elems_repr =
          convert_value(context, llvm_num_elems);
        tree range_type = NULL_TREE;

        gcc_assert(num_elems_repr);
        num_elems_tree = num_elems_repr->get_tree();

        range_type = build_range_type(sizetype,
                                      copy_node(size_zero_node),
                                      num_elems_tree);
        array_type = Tree::make_array_type(elem_type, range_type);
      }

    // Make the variable declaration.
    const std::string& llvm_alloca_name = llvm_alloca->getName();
    const std::string var_name = "alloca." + llvm_alloca_name;
    var_tree = Tree::build_var_decl(instr_loc, var_name,
                                    array_type, true);

    // LLVM alignment is in bits, GCC alignment is in bytes.
    if (llvm_alignment != 0)
      DECL_ALIGN(var_tree) = llvm_alignment * 8;

    // Add the variable to the function block.
    Tree::declare_var(var_tree,
                      curr_context->fn_repr->get_decln(),
                      curr_context->fn_repr->scope_tree);

    // Make the expression.
    expr_tree = fold_build1_loc(instr_loc, ADDR_EXPR,
                                expr_type, var_tree);

    instr_repr = new InstrRepr(llvm_instr, expr_tree, expr_tree);
    curr_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /* Convert a store instruction */
  InstrRepr*
  Parser::convert_instr_store(Context *context,
                              const llvm::Instruction *llvm_instr)
  {
    /*
     * Store a value
     * --------
     * [| Store <expr>, <addr> |]
     * -->
     * MODIFY_EXPR 
     *  (INDIRECT_REF [| <addr> |]),
     *  [| expr |]
     *
     */
    gcc_assert(llvm_instr);
    const llvm::StoreInst* llvm_store =
      static_cast<const llvm::StoreInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_store->getDebugLoc()));
    const llvm::Value* llvm_expr = llvm_store->getValueOperand();
    const llvm::Type* llvm_expr_type = llvm_expr->getType();
    const llvm::Value* llvm_addr = llvm_store->getPointerOperand();
    tree instr_tree = NULL_TREE;
    tree expr_tree = NULL_TREE;
    tree addr_tree = NULL_TREE;
    tree addr_type_tree = NULL_TREE;
    tree mem_ref_tree = NULL_TREE;
    ValueRepr* expr_repr = 0;
    ValueRepr* addr_repr = 0;
    TypeRepr* expr_type_repr = 0;
    InstrRepr* instr_repr = 0;

    // Convert the operands.
    expr_repr = convert_value(context, llvm_expr);
    gcc_assert(expr_repr && expr_repr->get_tree());
    expr_tree = expr_repr->get_tree();

    expr_type_repr = convert_type(context, llvm_expr_type);
    gcc_assert(expr_type_repr && expr_type_repr->get_tree());

    addr_repr = convert_value(context, llvm_addr);
    gcc_assert(addr_repr && addr_repr->get_tree());

    addr_tree = addr_repr->get_tree();
    gcc_assert(TREE_TYPE(addr_tree));

    addr_type_tree = TREE_TYPE(addr_tree);
    gcc_assert(POINTER_TYPE_P(addr_type_tree));

    // Make the memory reference.
    mem_ref_tree = build1_loc(instr_loc, 
                              INDIRECT_REF, addr_type_tree,
                              addr_tree);

    // Build the instruction
    instr_tree = fold_build2_loc(instr_loc, MODIFY_EXPR, 
                                 expr_type_repr->get_tree(),
                                 mem_ref_tree,
                                 expr_tree);

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /* Convert a load instruction */
  InstrRepr*
  Parser::convert_instr_load(Context *context,
                             const llvm::Instruction *llvm_instr)
  {
    /*
     * Load a value into a register
     * --------
     * [| Load <addr> |]
     * -->
     * INDIRECT_REF [| <addr> |]
     *
     */
    gcc_assert(llvm_instr);
    const llvm::LoadInst* llvm_load =
      static_cast<const llvm::LoadInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_load->getDebugLoc()));
    const llvm::Value* llvm_addr = llvm_load->getPointerOperand();
    const llvm::Type* llvm_type = llvm_load->getType();
    tree mem_ref_tree = NULL_TREE;
    InstrRepr* instr_repr = 0;
    TypeRepr* type_repr = 0;
    ValueRepr* addr_repr = 0;

    // Convert the operand.
    type_repr = convert_type(context, llvm_type);
    gcc_assert(type_repr && type_repr->get_tree());

    addr_repr = convert_value(context, llvm_addr);
    gcc_assert(addr_repr && addr_repr->get_tree());

    // Make the memory reference.
    mem_ref_tree = build1_loc(instr_loc,
                              INDIRECT_REF, type_repr->get_tree(),
                              addr_repr->get_tree());

    instr_repr = new InstrRepr(llvm_instr, mem_ref_tree, mem_ref_tree);
    curr_context->register_value_repr(instr_repr);
    return instr_repr;
  }

  /* Convert an LLVM constant integer to an unsigned int. */
  unsigned get_llvm_index(const llvm::ConstantInt* llvm_int)
  {
    gcc_assert(llvm_int);
    const llvm::APInt llvm_lo_value = llvm_int->getValue();
    unsigned int lo_word =
      static_cast<unsigned int>(*(llvm_lo_value.getRawData()));

    return lo_word;
  }

  /* Convert a type field reference. This is used to handle
     getelementptr and insertvalue/extractvalue instructions.
  */
  tree 
  Parser::convert_field_expr(Context *context,
                             const llvm::Value* llvm_value,
                             TypeFieldIndexSeq* index_seq)
  {
    /*
     * Make an expression to refer to a field in a type.
     * --------
     *
     * Base case:
     * C(<val>, <type>, {})
     * -->
     * (<val> : [| type |])
     *
     * Arrays and vectors:
     * C(<val>, <type>[], <expr>::<index_list>)
     * -->
     * C(ARRAY_REF(val, [| expr |]), <type>, <index_list>)
     * 
     * Pointers:
     * C(<val>, <type>*, <expr>::<index_list>)
     * -->
     * C(INDIRECT_REF(PLUS_EXPR(val, [| expr |])), <type>, <index_list>)
     *
     * Structures:
     * C(<val>, {type_(1), .., }, <index>::<index_list>)
     * -->
     * C(COMPONENT_REF(val, index), <type_(index)>, <index_list>)
     *
     */
    gcc_assert(llvm_value);
    gcc_assert(index_seq);
    ObjContext* const curr_context = context->curr_context;
    const llvm::Type* llvm_type = llvm_value->getType();
    const llvm::Type* llvm_subtype = 0;
    const location_t instr_loc = curr_context->src_loc;
    ValueRepr* val_repr = 0;
    TypeRepr* subtype_repr = 0;
    tree val_tree = NULL_TREE;

    // Convert the type and value.
    gcc_assert(llvm_type);
    val_repr = convert_value(context, llvm_value);
    gcc_assert(val_repr && val_repr->get_tree());

    // Iterate through the indices building up val_tree.
    val_tree = val_repr->get_tree();
    llvm_subtype = llvm_type;
    subtype_repr = convert_type(context, llvm_subtype);

    for (TypeFieldIndexSeq::const_iterator 
           index_itr = index_seq->begin(),
           index_end = index_seq->end();
         index_itr != index_end;
         ++index_itr)
      {
        const TypeFieldIndex& field_index = *index_itr;
        const llvm::Value* llvm_expr = 0;
        const llvm::Type* llvm_elem_type = 0;
        size_t expr_index = 0;
        tree expr_tree = NULL_TREE;
        tree new_val_tree = NULL_TREE;
        TypeRepr* elem_type_repr = 0;

        gcc_assert(val_tree);
        gcc_assert(llvm_subtype);
        gcc_assert(subtype_repr);

        // Convert the indexing expression, extracting constant
        // integers from artificial operands.
        if (field_index.is_integer)
          {
            expr_index = field_index.index.value;
            expr_tree = build_int_cst_type(integer_type_node, expr_index);
          }
        else
          {
            ValueRepr* expr_repr = 0;
            llvm_expr = field_index.index.llvm_value;
            expr_repr = convert_value(context, llvm_expr);
            gcc_assert(expr_repr && expr_repr->get_tree());
            expr_tree = expr_repr->get_tree();
          }

        // Type specific references.
        if (llvm_subtype->isArrayTy() 
            || llvm_subtype->isVectorTy())
          {
            const llvm::SequentialType* llvm_seq_type =
              static_cast<const llvm::SequentialType*>(llvm_subtype);

            llvm_elem_type = llvm_seq_type->getElementType();
            gcc_assert(llvm_elem_type);

            elem_type_repr = convert_type(context, llvm_elem_type);
            gcc_assert(elem_type_repr && elem_type_repr->get_tree());

            new_val_tree = build4_loc(instr_loc,
                                      ARRAY_REF,
                                      elem_type_repr->get_tree(),
                                      val_tree,
                                      expr_tree,
                                      NULL_TREE, NULL_TREE);
          }
        else if (llvm_subtype->isPointerTy())
          {
            const llvm::PointerType* llvm_ptr_type =
              static_cast<const llvm::PointerType*>(llvm_subtype);
            tree ptr_index_tree = NULL_TREE;
            tree offset_tree = NULL_TREE;

            llvm_elem_type = llvm_ptr_type->getElementType();
            gcc_assert(llvm_elem_type);

            elem_type_repr = convert_type(context, llvm_elem_type);
            gcc_assert(elem_type_repr && elem_type_repr->get_tree());

            ptr_index_tree = fold_convert_loc(instr_loc, size_type_node,
                                              expr_tree);
            offset_tree = fold_build2_loc(instr_loc,
                                          POINTER_PLUS_EXPR,
                                          subtype_repr->get_tree(),
                                          val_tree, ptr_index_tree);

            new_val_tree = build1_loc(instr_loc,
                                      INDIRECT_REF,
                                      elem_type_repr->get_tree(),
                                      offset_tree);
          }
        else if (llvm_subtype->isStructTy())
          {
            StructTypeRepr* struct_repr = 0;
            FieldRepr* field_repr = 0;

            gcc_assert(field_index.is_integer || llvm_expr);

            if ((!field_index.is_integer)
                && llvm::isa<llvm::ConstantInt>(*llvm_expr))
              {
                const llvm::ConstantInt* llvm_index =
                  static_cast<const llvm::ConstantInt*>(llvm_expr);
                expr_index = get_llvm_index(llvm_index);
              }
            else
              {
                // The index for a structure must always be a constant.
                gcc_assert(field_index.is_integer);
              }

            // Get the field matching the indexed LLVM field.
            gcc_assert(subtype_repr->get_kind() == TypeRepr::STRUCT);
            struct_repr = static_cast<StructTypeRepr*>(subtype_repr);

            gcc_assert(expr_index < struct_repr->fields.size());

            field_repr = struct_repr->fields[expr_index];
            gcc_assert(field_repr 
                       && field_repr->get_tree()
                       && field_repr->get_type()
                       && field_repr->decln);
              
            // Set the element type from the field.
            elem_type_repr = field_repr->subtype;
            gcc_assert(elem_type_repr && elem_type_repr->get_tree());

            llvm_elem_type = elem_type_repr->get_type();
            gcc_assert(llvm_elem_type);

            new_val_tree = build3_loc(instr_loc,
                                      COMPONENT_REF, 
                                      elem_type_repr->get_tree(),
                                      val_tree, field_repr->decln,
                                      NULL_TREE);
          }
        else
          {
            // Should never get here.
            error_at(instr_loc,
                     "invalid type in LLVM extractvalue instruction.");
            gcc_unreachable();
          }

        // Advance the conversion to the element of the aggregate type.
        val_tree = new_val_tree;
        llvm_subtype = llvm_elem_type;
        subtype_repr = elem_type_repr;
      }

    return val_tree;
  }

  /* Convert a type field index. This is used to handle extractvalue
     and insertvalue instructions.*/
  tree 
  Parser::convert_field_index(Context *context,
                              const llvm::Value* llvm_value,
                              const unsigned num_indices,
                              const unsigned *field_index_begin,
                              const unsigned *field_index_end)
  {
    /*
     * Get a field in a type by its index.
     * --------
     * 
     * Constructs a vector of llvm::ConstantInt, <operand-list> from
     * the <index-list>, and calls convert_field_expr.
     * 
     */
    gcc_assert(llvm_value);
    TypeFieldIndexSeq llvm_operand_seq;
    
    llvm_operand_seq.reserve(num_indices);
    for (const unsigned *itr = field_index_begin;
         itr != field_index_end;
         ++itr)
      {
        const unsigned field_index = *itr;
        llvm_operand_seq.push_back(TypeFieldIndex(field_index));
      }

    return convert_field_expr(context, llvm_value, &llvm_operand_seq);
  }


  /* Convert an extractelement instruction. */
  InstrRepr* 
  Parser::convert_instr_extractelement(Context *context,
                                       const llvm::Instruction *llvm_instr)
  {
    /*
     * Get the value of a field in a type.
     * --------
     * [| extractelement <type> <vector>, <index> |]
     * -->
     * ARRAY_REF [| <vector> |],  [| < index > |]
     *
     */
    gcc_assert(llvm_instr);
    const llvm::ExtractElementInst* llvm_extract =
      static_cast<const llvm::ExtractElementInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Value* llvm_vector = llvm_extract->getVectorOperand();
    const llvm::Type* llvm_op1_type = llvm_vector->getType();
    const llvm::Value* llvm_index = llvm_extract->getIndexOperand();
    gcc_assert(llvm_op1_type->isVectorTy());
    ValueRepr* vector_repr = 0;
    ValueRepr* index_repr = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;
    gcc_assert(instr_tree);

    vector_repr = convert_value(context, llvm_vector);
    index_repr = convert_value(context, llvm_index);

    gcc_assert(vector_repr && vector_repr->get_tree());
    gcc_assert(index_repr && index_repr->get_tree());

    instr_tree = Tree::make_vector_ref(instr_loc,
                                       vector_repr->get_tree(),
                                       index_repr->get_tree());

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* VecMapInsert(pos, e, vec): Insert element e into vector vec
     index pos. */
  class VecMapInsert: public Tree::VecMapPred
  {
  public:
    tree pos;

    explicit 
    VecMapInsert(location_t l, tree ty, tree p, tree e1, tree e2)
      : Tree::VecMapPred(l, ty, e1, e2), pos(p)
    { /* skip */ }

    tree pred(const unsigned idx)
    {
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      tree idx_type = TREE_TYPE(idx_tree);

      return fold_build2_loc(loc, EQ_EXPR, idx_type, pos, idx_tree);
    }

    tree true_result(const unsigned idx ATTRIBUTE_UNUSED)
    {
      return expr1;
    }
  };

  /* Convert an insertelement instruction. */
  InstrRepr*
  Parser::convert_instr_insertelement(Context *context,
                                      const llvm::Instruction *llvm_instr)
  {
    /*
     * Get the value of a field in a type.
     * --------
     * [| insertelement <vec-type> <vector>, <value>, <index> |]
     * -->
     * CONSTRUCTOR <vec-type>
     * {
     *   ..
     *   idx: COND_EXPR 
     *          (idx == [| index |]), 
     *          [| value |],
     *          ARRAY_REF [| vector |] idx
     *   ..
     * }
     */
    gcc_assert(llvm_instr);
    const llvm::InsertElementInst* llvm_insert =
      static_cast<const llvm::InsertElementInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Value* llvm_vector = llvm_insert->getOperand(0);
    const llvm::Value* llvm_value = llvm_insert->getOperand(1);
    const llvm::Value* llvm_index = llvm_insert->getOperand(2);
    const llvm::Type* llvm_op1_type = llvm_vector->getType();
    gcc_assert(llvm_op1_type->isVectorTy());
    const llvm::VectorType* llvm_vec_type = 
      static_cast<const llvm::VectorType*>(llvm_op1_type);
    const unsigned num_elems = llvm_vec_type->getNumElements();
    const llvm::Type* llvm_base_type = llvm_vec_type->getElementType();
    TypeRepr* base_type = 0;
    ValueRepr* vector_repr = 0;
    ValueRepr* value_repr = 0;
    ValueRepr* index_repr = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    Tree::VecMapper* mapper = 0;

    curr_context->src_loc = instr_loc;
    gcc_assert(instr_tree);

    base_type = convert_type(context, llvm_base_type);
    vector_repr = convert_value(context, llvm_vector);
    value_repr = convert_value(context, llvm_value);
    index_repr = convert_value(context, llvm_index);

    gcc_assert(base_type && base_type->get_tree());
    gcc_assert(vector_repr && vector_repr->get_tree());
    gcc_assert(value_repr && value_repr->get_tree());
    gcc_assert(index_repr && index_repr->get_tree());

    mapper = new VecMapInsert(instr_loc, 
                              base_type->get_tree(),
                              index_repr->get_tree(),
                              value_repr->get_tree(),
                              vector_repr->get_tree());
    instr_tree = Tree::make_mapped_vector(base_type->get_tree(),
                                          num_elems,
                                          mapper);
    delete(mapper); mapper = 0;

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Select shuffle elements from two vectors. */
  class VecShuffleTwoMap: public Tree::VecMapper
  {
  public:
    location_t loc;
    tree vector1;
    tree vector2;
    const llvm::ShuffleVectorInst* llvm_shuffle;
    int vec_size;

    explicit
    VecShuffleTwoMap(location_t l, tree e1, tree e2, 
                     const llvm::ShuffleVectorInst* inst,
                     unsigned s)
      : loc(l), vector1(e1), vector2(e2), 
        llvm_shuffle(inst), vec_size(s)
    { /* skip */ }

    tree build(const unsigned idx)
    {
      tree vec_tree = NULL_TREE;
      tree pos_tree = NULL_TREE;
      int pos = llvm_shuffle->getMaskValue(idx);

      if (pos < 0)
        {
          pos_tree = build_int_cst_type(integer_type_node, 0);        
          vec_tree = vector1;
        }
      else if (pos < vec_size)
        {
          pos_tree = build_int_cst_type(integer_type_node, pos);
          vec_tree = vector1;
        }
      else
        {
          pos_tree = build_int_cst_type(integer_type_node, pos - vec_size);
          vec_tree = vector2;          
        }

      return Tree::make_vector_ref(loc, vec_tree, pos_tree);
    }
  };

  /* Select shuffle elements from one vector. */
  class VecShuffleOneMap: public Tree::VecMapper
  {
  public:
    location_t loc;
    tree vector1;
    const llvm::ShuffleVectorInst* llvm_shuffle;

    explicit
    VecShuffleOneMap(location_t l, tree e1,
                     const llvm::ShuffleVectorInst* inst)
      : loc(l), vector1(e1), llvm_shuffle(inst)
    { /* skip */ }

    tree build(const unsigned idx)
    {
      tree pos_tree = NULL_TREE;
      int pos = llvm_shuffle->getMaskValue(idx);

      // Undefined selector chooses to position 0.
      if (pos < 0)
        pos = 0;

      pos_tree = build_int_cst_type(integer_type_node, pos);
      return Tree::make_vector_ref(loc, vector1, pos_tree);
    }
  };

  /* Convert a shufflevector instruction. */
  InstrRepr* 
  Parser::convert_instr_shufflevector(Context *context,
                                      const llvm::Instruction *llvm_instr)
  {
    /*
     * Get the value of a field in a type.
     * --------
     * Shuffling one vectors:
     *
     * [| shufflevector <vector-type> <vector>, <mask> |]
     * -->
     * CONSTRUCTOR <vec-type>
     * {
     *   ..
     *  idx: ARRAY_REF [| vector |], mask[idx]
     *   ..
     * }
     *
     * Shuffling two vectors:
     *
     * [| shufflevector <vector-type(size)> <vector1>, <vector2>, <mask> |]
     * -->
     * CONSTRUCTOR <vec-type>
     * {
     *   ..
     *  idx: if (mask[idx] < size) 
     then ARRAY_REF [| vector1 |], mask[idx]
     else ARRAY_REF [| vector2 |], (size - mask[idx])
     *   ..
     * }
     *
     */
    gcc_assert(llvm_instr);
    Tree::VecMapper* mapper = 0;
    const llvm::ShuffleVectorInst* llvm_shuffle =
      static_cast<const llvm::ShuffleVectorInst*>(llvm_instr);

    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Value* llvm_vector1 = llvm_shuffle->getOperand(0);
    const llvm::Value* llvm_vector2 = llvm_shuffle->getOperand(1);
    const llvm::Value* llvm_mask = llvm_shuffle->getOperand(2);

    const llvm::Type* llvm_mask_op_type = llvm_mask->getType();
    gcc_assert(llvm_mask_op_type->isVectorTy());
    const llvm::VectorType* llvm_mask_type = 
      static_cast<const llvm::VectorType*>(llvm_mask_op_type);
    const unsigned num_mask_elems = llvm_mask_type->getNumElements();

    const llvm::Type* llvm_vec1_op_type = llvm_vector1->getType();
    gcc_assert(llvm_vec1_op_type->isVectorTy());
    const llvm::VectorType* llvm_vector1_type = 
      static_cast<const llvm::VectorType*>(llvm_vec1_op_type);
    const llvm::Type* llvm_vec_base_type = 
      llvm_vector1_type->getElementType();
    const unsigned num_vec_elems = llvm_vector1_type->getNumElements();

    TypeRepr* base_type = 0;
    ValueRepr* vector1_repr = 0;
    ValueRepr* vector2_repr = 0;
    ValueRepr* mask_repr = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    const bool is_unary_shuffle = llvm::isa<llvm::UndefValue>(*llvm_vector2);

    curr_context->src_loc = instr_loc;
    gcc_assert(instr_tree);

    // Convert operands
    base_type = convert_type(context, llvm_vec_base_type);
    mask_repr = convert_value(context, llvm_mask);
    vector1_repr = convert_value(context, llvm_vector1);

    gcc_assert(base_type && base_type->get_tree());
    gcc_assert(vector1_repr && vector1_repr->get_tree());
    gcc_assert(mask_repr && mask_repr->get_tree());

    // Choose a mapper based on unary/binary shuffles.
    if (is_unary_shuffle)
      {
        // Unary shuffles.
        mapper = new VecShuffleOneMap(instr_loc,
                                      vector1_repr->get_tree(),
                                      llvm_shuffle);
      }
    else
      {
        // Binary shuffles.
        vector2_repr = convert_value(context, llvm_vector2);
        gcc_assert(vector2_repr && vector2_repr->get_tree());

        mapper = new VecShuffleTwoMap(instr_loc,
                                      vector1_repr->get_tree(),
                                      vector2_repr->get_tree(),
                                      llvm_shuffle,
                                      num_vec_elems);
      }

    instr_tree = Tree::make_mapped_vector(base_type->get_tree(),
                                          num_mask_elems,
                                          mapper);
    delete(mapper); mapper = 0;

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert an extractvalue instruction. */
  InstrRepr*
  Parser::convert_instr_extractvalue(Context *context,
                                     const llvm::Instruction *llvm_instr)
  {
    /*
     * Get the value of a field in a type.
     * --------
     * [| extractvalue <type> <object>, <index>+ |]
     * -->
     * convert_field_index (<object>, <type>, <index>+)
     *
     */
    gcc_assert(llvm_instr);
    const llvm::ExtractValueInst* llvm_extractvalue =
      static_cast<const llvm::ExtractValueInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Value* llvm_operand = llvm_extractvalue->getAggregateOperand();
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;

    gcc_assert(llvm_operand);

    curr_context->src_loc = instr_loc;
    instr_tree = convert_field_index(context,
                                     llvm_operand,
                                     llvm_extractvalue->getNumIndices(),
                                     llvm_extractvalue->idx_begin(),
                                     llvm_extractvalue->idx_end());
    gcc_assert(instr_tree);

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert an insertvalue instruction. */
  InstrRepr*
  Parser::convert_instr_insertvalue(Context *context,
                                    const llvm::Instruction *llvm_instr)
  {
    /*
     * Set the value of a field in an aggregate type.
     * --------
     * [| insertvalue <type> <object>, <val>, <index>+ |]
     * -->
     * SAVE_EXPR(
     *   MODIFY_EXPR(convert_field_index (<object>, <type>, <index>+),
     *              [| val |]))
     *
     */
    gcc_assert(llvm_instr);
    const llvm::InsertValueInst* llvm_insertvalue =
      static_cast<const llvm::InsertValueInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Value* llvm_lhs = llvm_insertvalue->getAggregateOperand();
    const llvm::Value* llvm_rhs = llvm_insertvalue->getInsertedValueOperand();
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    ValueRepr* lhs_repr = 0;
    tree lhs_tree = NULL_TREE;
    ValueRepr* rhs_repr = 0;
 
    gcc_assert(llvm_lhs);
    gcc_assert(llvm_rhs);

    curr_context->src_loc = instr_loc;

    // Convert the new value of the field.
    rhs_repr = convert_value(context, llvm_rhs);
    gcc_assert(rhs_repr && rhs_repr->get_tree());

    // Convert the lhs specifier.
    lhs_tree = convert_field_index(context,
                                   llvm_lhs,
                                   llvm_insertvalue->getNumIndices(),
                                   llvm_insertvalue->idx_begin(),
                                   llvm_insertvalue->idx_end());
    gcc_assert(lhs_tree);
    lhs_repr = convert_value(context, llvm_lhs);
    gcc_assert(lhs_repr && lhs_repr->get_tree());

    // Make the new instruction
    instr_tree = fold_build2_loc(instr_loc,
                                 MODIFY_EXPR, void_type_node,
                                 lhs_tree, rhs_repr->get_tree());

    instr_repr = new InstrRepr(llvm_instr, lhs_repr->get_tree(), instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert a getelementptr instruction. */
  InstrRepr*
  Parser::convert_instr_getelementptr(Context *context,
                                      const llvm::Instruction *llvm_instr)
  {
    /*
     * Get a pointer to a field of a type.
     * --------
     * [| getelementptr <type>* <ptr>, <expr>+ |]
     * -->
     * ADDR_EXPR(convert_field_expr (<ptr>, <type>, true, <expr>+, false))
     *
     */
    gcc_assert(llvm_instr);
    const llvm::GetElementPtrInst* llvm_gep =
      static_cast<const llvm::GetElementPtrInst*>(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::Value* llvm_ptr = llvm_gep->getPointerOperand();
    gcc_assert(llvm_ptr);
    const llvm::Type* llvm_type = llvm_ptr->getType();
    gcc_assert(llvm_type);
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;
    tree expr_tree = NULL_TREE;
    TypeFieldIndexSeq llvm_operand_seq;

    curr_context->src_loc = instr_loc;

    // Set up the field index sequence.
    llvm_operand_seq.reserve(llvm_gep->getNumIndices());
    for (llvm::User::const_op_iterator
           index_itr = llvm_gep->idx_begin(),
           index_end = llvm_gep->idx_end();
         index_itr != index_end;
         ++index_itr)
      {
        const llvm::Value* llvm_field_index = *index_itr;
        llvm_operand_seq.push_back(TypeFieldIndex(llvm_field_index));
      }

    expr_tree = convert_field_expr(context, llvm_ptr,
                                   &llvm_operand_seq);
    gcc_assert(expr_tree);
    gcc_assert(TREE_TYPE(expr_tree));

    instr_tree = fold_build1_loc(instr_loc, ADDR_EXPR,
                                 build_pointer_type(TREE_TYPE(expr_tree)),
                                 expr_tree);


    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert an LLVM predicate for a give type. */
  tree_code
  Parser::convert_cmp_predicate(Context *context ATTRIBUTE_UNUSED,
                                const llvm::CmpInst::Predicate llvm_pred,
                                const llvm::Type* llvm_type)
  {
    /*
     * Integer predicates
     * --------
     *  
     *
     */
    gcc_assert(llvm_type);
    ObjContext* const curr_context = context->curr_context;
    tree_code pred_code = ERROR_MARK;
    static const tree_code int_pred[(llvm::CmpInst::LAST_ICMP_PREDICATE
                                     - llvm::CmpInst::FIRST_ICMP_PREDICATE) + 1] =
      {
        EQ_EXPR, // ICMP_EQ
        NE_EXPR, // ICMP_NE
        GT_EXPR, // ICMP_UGT
        GE_EXPR, // ICMP_UGE
        LT_EXPR, // ICMP_ULT
        LE_EXPR, // ICMP_ULE
        GT_EXPR, // ICMP_SGT
        GE_EXPR, // ICMP_SGE
        LT_EXPR, // ICMP_SLT
        LE_EXPR  // ICMP_SLE
      };
    static const tree_code fp_pred[(llvm::CmpInst::LAST_FCMP_PREDICATE
                                    - llvm::CmpInst::FIRST_FCMP_PREDICATE) + 1] =
      {
        ERROR_MARK, // FCMP_FALSE (Must be handled as a special case)
        EQ_EXPR, // FCMP_OEQ
        GT_EXPR, // FCMP_OGT
        GE_EXPR, // FCMP_OGE
        LT_EXPR, // FCMP_OLT
        LE_EXPR, // FCMP_OLE
        ERROR_MARK, // FCMP_ONE (Must be handled as a special case)
        ORDERED_EXPR,   // FCMP_ORD
        UNORDERED_EXPR, // FCMP_UNO
        UNEQ_EXPR, // FCMP_UEQ
        UNGT_EXPR, // FCMP_UGT
        UNGE_EXPR, // FCMP_UGE
        UNLT_EXPR, // FCMP_ULT
        UNLE_EXPR, // FCMP_ULE
        NE_EXPR,   // FCMP_UNE
        ERROR_MARK    // FCMP_TRUE (Must be handled as a special case)
      };

    if (llvm_type->isIntOrIntVectorTy()
        || llvm_type->isPointerTy())
      {
        size_t idx = 0;

        if (llvm_pred < llvm::CmpInst::FIRST_ICMP_PREDICATE
            || llvm::CmpInst::LAST_ICMP_PREDICATE < llvm_pred)
          {
            error_at(curr_context->src_loc,
                     "invalid integer comparison.");
            gcc_unreachable();
          }
        idx = llvm_pred - llvm::CmpInst::FIRST_ICMP_PREDICATE;
        pred_code = int_pred[idx];
      }
    else if (llvm_type->isFPOrFPVectorTy())
      {
        size_t idx = 0;

        if (llvm_pred < llvm::CmpInst::FIRST_FCMP_PREDICATE
            || llvm::CmpInst::LAST_FCMP_PREDICATE < llvm_pred)
          {
            error_at(curr_context->src_loc,
                     "invalid integer comparison.");
            gcc_unreachable();
          }
        idx = llvm_pred - llvm::CmpInst::FIRST_FCMP_PREDICATE;
        pred_code = fp_pred[idx];
      }
    else
      {
        // Only integers, pointers, and floating point types are supported in
        // LLVM comparisons.
        error_at(curr_context->src_loc,
                 "invalid type in LLVM comparison instruction.");
        gcc_unreachable();
      }

    return pred_code;
  }

  /* Vector operation mapper, for always true or false. */
  class VecMapBool : public Tree::VecMapper
  {
  public:
    // Fields
    location_t loc;
    tree value;

    // Constructor
    explicit VecMapBool(location_t l, bool v)
      : loc(l), value(NULL_TREE)
    { 
      value = Tree::make_bool_tree(v);
    }

    tree build(const unsigned idx ATTRIBUTE_UNUSED)
    {
      return value;
    }
  };

  /* Vector operation mapper, for ordered-not-equal. */
  class VecMapONE : public Tree::VecMapper
  {
  public:
    // Fields
    location_t loc;
    tree expr1;
    tree expr2;

    // Constructor
    explicit VecMapONE(location_t l, tree e1, tree e2)
      : loc(l), expr1(e1), expr2(e2)
    { /* skip */ }

    tree build(const unsigned idx)
    {
      tree element_tree = NULL_TREE;
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      tree expr1_ref_tree = 
        Tree::make_vector_ref(loc, expr1, idx_tree);
      tree expr2_ref_tree = 
        Tree::make_vector_ref(loc, expr2, idx_tree);
      tree order_tree = fold_build2_loc(loc,
                                        ORDERED_EXPR,
                                        boolean_type_node,
                                        expr1_ref_tree,
                                        expr2_ref_tree);
          
      tree neq_tree = fold_build2_loc(loc,
                                      NE_EXPR,
                                      boolean_type_node,
                                      expr1_ref_tree,
                                      expr2_ref_tree);

      element_tree = fold_build2_loc(loc,
                                     TRUTH_ANDIF_EXPR,
                                     boolean_type_node,
                                     order_tree,
                                     neq_tree);

      return element_tree;
    }
  };


  /* Convert an cmp instruction. This is the worker function for
     convert_instr_icmp and convert_instr_fcmp.  */
  InstrRepr*
  Parser::convert_instr_cmp(Context *context,
                            const llvm::Instruction* llvm_instr)
  {
    /*
     * Compare two expressions
     * --------
     * [| icmp <type> <pred>, <expr1>, <expr2> |]
     * [| fcmp <type> <pred>, <expr1>, <expr2> |]
     * -->
     * If <type> is not a vector:
     * [| pred |]([| expr1 |], [| expr2 |]), otherwise
     *
     * If <type> is a vector,
     * COND_EXPR(([| pred |] [| expr1 |], [| expr2 |]),
     *            true_vector, false_vector)
     * where true_vector, false_vector are boolean vectors of the same size
     *        as <type> with the elements set to all true and all false
     *        respectively.
     */
    gcc_assert(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    const llvm::CmpInst* llvm_cmp =
      static_cast<const llvm::CmpInst*>(llvm_instr);
    location_t instr_loc =
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::CmpInst::Predicate llvm_pred = llvm_cmp->getPredicate();
    gcc_assert(llvm_pred);
    const llvm::Type* llvm_type = llvm_cmp->getType();
    gcc_assert(llvm_type);
    const llvm::Value* llvm_expr1 = llvm_cmp->getOperand(0);
    gcc_assert(llvm_expr1);
    const llvm::Value* llvm_expr2 = llvm_cmp->getOperand(1);
    gcc_assert(llvm_expr2);
    const llvm::Type* llvm_expr_type = llvm_expr1->getType();
    gcc_assert(llvm_expr_type);
    TypeRepr* type_repr = 0;
    InstrRepr* instr_repr = 0;
    ValueRepr* expr1_repr = 0;
    ValueRepr* expr2_repr = 0;
    tree expr_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;

    // Convert the operands
    type_repr = convert_type(context, llvm_type);
    expr1_repr = convert_value(context, llvm_expr1);
    expr2_repr = convert_value(context, llvm_expr2);

    gcc_assert(type_repr && type_repr->get_tree());
    gcc_assert(expr1_repr && expr1_repr->get_tree());
    gcc_assert(expr2_repr && expr2_repr->get_tree());

    // Some predicates must be handled as a special case, most have direct
    // translations to GCC.
    //
    // Operations on vectors have to wrapped in a constructor, so
    // vectors are handled seperately.
    if (!llvm_expr_type->isVectorTy())
      {
        // Atomic operations.
        switch (llvm_pred)
          {
          case llvm::CmpInst::FCMP_FALSE:
            // Always false comparison between expressions.
            // ------
            // [| cmp FCMP_FALSE, <expr1>, <expr2> |]
            // -->
            // FALSE_EXPR
            //
            expr_tree = Tree::make_bool_tree(false);
            break;
          case llvm::CmpInst::FCMP_TRUE:
            // Always TRUE comparison between expressions.
            // ------
            // [| cmp FCMP_FALSE, <expr1>, <expr2> |]
            // -->
            // TRUE_EXPR
            //
            expr_tree = Tree::make_bool_tree(true);
            break;
          case llvm::CmpInst::FCMP_ONE:
            {
              // Ordered Not-Equal between Floating ppoint types.
              // ------
              // [| cmp FCMP_ONE, <expr1>, <expr2> |]
              // -->
              // TRUTH_ANDIF_EXPR(ORDERED_EXPR( [| expr1 |], [| expr2 |]),
              //                  NEQ_EXPR( [| expr1 |], [| expr2 |]))
              //
              tree order_tree;
              tree neq_tree;
          
              order_tree = fold_build2_loc(instr_loc,
                                           ORDERED_EXPR,
                                           boolean_type_node,
                                           expr1_repr->get_tree(),
                                           expr2_repr->get_tree());
          
              neq_tree = fold_build2_loc(instr_loc,
                                         NE_EXPR,
                                         boolean_type_node,
                                         expr1_repr->get_tree(),
                                         expr2_repr->get_tree());

              expr_tree = fold_build2_loc(instr_loc,
                                          TRUTH_ANDIF_EXPR,
                                          boolean_type_node,
                                          order_tree,
                                          neq_tree);

              break;
            }
          default:
            {
              // General case, convert the predicate and build the condition
              // expression around that.
              tree_code pred_code = ERROR_MARK;

              pred_code =
                convert_cmp_predicate(context, llvm_pred, llvm_expr_type);
              expr_tree = fold_build2_loc(instr_loc,
                                          pred_code, boolean_type_node,
                                          expr1_repr->get_tree(),
                                          expr2_repr->get_tree());
              break;
            }
          }
        gcc_assert(expr_tree);
      }
    else
      {
        // Vector operations.
        const llvm::VectorType* llvm_vector_type =
          static_cast<const llvm::VectorType*>(llvm_expr_type);
        const unsigned num_elems = llvm_vector_type->getNumElements();
        tree expr1_tree = expr1_repr->get_tree();
        tree expr2_tree = expr2_repr->get_tree();
        Tree::VecMapper *mapper = 0;

        switch(llvm_pred)
          {
          case llvm::CmpInst::FCMP_FALSE:
            mapper = new Tree::VecMapConst(instr_loc,
                                           Tree::make_bool_tree(false));
            break;
          case llvm::CmpInst::FCMP_TRUE:
            mapper = new Tree::VecMapConst(instr_loc,
                                           Tree::make_bool_tree(true));

            break;
          case llvm::CmpInst::FCMP_ONE:
            mapper = new VecMapONE(instr_loc, expr1_tree, expr2_tree);
            break;
          default:
            {
              tree_code pred_code = ERROR_MARK;
              pred_code = convert_cmp_predicate(context,
                                                llvm_pred, llvm_expr_type);

              mapper = new Tree::VecMapBinOp(instr_loc, 
                                             pred_code,
                                             boolean_type_node,
                                             expr1_tree,
                                             expr2_tree);
              break;
            }
          }
        gcc_assert(mapper);

        // Build the constructor
        expr_tree = Tree::make_mapped_vector(boolean_type_node,
                                             num_elems,
                                             mapper);

        delete(mapper); mapper = 0;
      }

    instr_repr = new InstrRepr(llvm_instr, expr_tree, expr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Convert a PHI instruction */
  InstrRepr*
  Parser::convert_instr_phi(Context *context,
                            const llvm::Instruction *llvm_instr)
  {
    /*
     * Phi nodes:
     * --------
     * [| phi <ty> [<val0>, <label0>] , .., [<valn>, <labeln>] |]
     * -->
     * VAR_DECL phi_var
     * ..
     * labeli:
     * {
     *   ..
     *   MODIFY_EXPR (phi_var, [| vali |])
     *   block_terminator
     *  }
     */
    assert(llvm_instr);
    FnContext* const curr_context = FnContext::from(context->curr_context);
    const llvm::PHINode* llvm_phi =
      static_cast<const llvm::PHINode*>(llvm_instr);
    location_t instr_loc =
      convert_location(context, &(llvm_phi->getDebugLoc()));
    const llvm::Type* llvm_expr_type = llvm_phi->getType();
    const unsigned num_values = llvm_phi->getNumIncomingValues();
    TypeRepr* expr_type_repr = 0;
    InstrRepr* instr_repr = 0;
    tree var_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;

    // Convert the LLVM components.
    expr_type_repr = convert_type(context, llvm_expr_type);
    gcc_assert(expr_type_repr && expr_type_repr->get_tree());

    // Make the variable declaration.
    std::string var_id_string = llvm_phi->getName();
    var_tree = build_decl(instr_loc, 
                          VAR_DECL, NULL_TREE, 
                          expr_type_repr->get_tree());
    DECL_ARTIFICIAL(var_tree) = 1;
    DECL_NAME(var_tree) = Tree::make_name_tree("phi", true);

    Tree::declare_var(var_tree,
                      curr_context->fn_repr->get_decln(),
                      curr_context->fn_repr->scope_tree);

    // Add bindings for the new variable in each of the incoming
    // blocks.
    for(unsigned idx = 0; idx < num_values; idx += 1)
      {
        const llvm::Value* llvm_value =
          llvm_phi->getIncomingValue(idx);
        const llvm::BasicBlock* llvm_block = 
          llvm_phi->getIncomingBlock(idx);
        ValueRepr* value_repr = 0;
        BlockRepr* block_repr = 0;
        tree bind_tree = NULL_TREE;
        InstrRepr* code_repr = 0;

        value_repr = convert_value(context, llvm_value);
        block_repr = 
          static_cast<BlockRepr*>(convert_value(context, llvm_block));

        gcc_assert(value_repr && value_repr->get_tree());
        gcc_assert(block_repr && block_repr->get_tree());

        // Assign the value to var_tree at the end of the block.
        bind_tree = fold_build2(MODIFY_EXPR,
                                expr_type_repr->get_tree(),
                                var_tree,
                                value_repr->get_tree());

        code_repr = new InstrRepr(llvm_instr, bind_tree, bind_tree);

        block_repr->extra_end_instrs.push_back(code_repr);
      }


    // Return the variable declaration as the expression to use.
    instr_repr = new InstrRepr(llvm_instr, var_tree, var_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Mapper to apply a selection to a vector. 
     Applies the operation (sel[idx] ? expr1[idx]: expr2[idx]).
  */
  class VecMapSelect: public Tree::VecMapPred
  {
  public:
    tree sel;

    explicit 
    VecMapSelect(location_t l, tree ty, tree s, tree e1, tree e2)
      : Tree::VecMapPred(l, ty, e1, e2), sel(s)
    { /* skip */ }

    tree pred(const unsigned idx)
    {
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      return Tree::make_vector_ref(loc, sel, idx_tree);
    }
  };

  /* Convert a select instruction. */
  InstrRepr* 
  Parser::convert_instr_select(Context *context,
                               const llvm::Instruction* llvm_instr)
  {
    /*
     * Conditional assignment
     * --------
     * [| select <scalar_type> <cond>, <expr1>, <expr2> |]
     * -->
     * COND_EXPR ([| cond |] == TRUE), [| expr1 |], [| expr2 |]
     *
     * [| select <vector_type> <cond>, <expr1>, <expr2> |]
     * -->
     * COND_EXPR ([| cond |] OR FALSE), [| expr1 |], [| expr2 |]
     *
     *
     * [| select <vector_type> <cond>, <expr1>, <expr2> |]
     * -->
     * CONSTRUCTOR [| vector_type |]
     * {
     *   ....
     *   COND_EXPR ([| cond[i] |]), [| expr1[i] |], [| expr2[i] |]
     *   ...
     * }
     *
     */
    gcc_assert(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc = 
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::SelectInst* llvm_select = 
      static_cast<const llvm::SelectInst*>(llvm_instr);
    const llvm::Value* llvm_cond = llvm_select->getCondition();
    gcc_assert(llvm_cond);
    const llvm::Type* llvm_type = llvm_select->getType();
    gcc_assert(llvm_type);
    const llvm::Value* llvm_expr1 = llvm_select->getTrueValue();
    gcc_assert(llvm_expr1);
    const llvm::Value* llvm_expr2 = llvm_select->getFalseValue();
    gcc_assert(llvm_expr2);
    const llvm::Type* llvm_cond_type = llvm_cond->getType();
    gcc_assert(llvm_cond_type);
    const llvm::Type* llvm_expr_type = llvm_expr1->getType();
    gcc_assert(llvm_expr_type);
    TypeRepr* cond_type_repr = 0;
    InstrRepr* instr_repr = 0;
    ValueRepr* cond_repr = 0;
    ValueRepr* expr1_repr = 0;
    ValueRepr* expr2_repr = 0;
    tree_code instr_opcode = ERROR_MARK;
    tree instr_tree = NULL_TREE;
    tree pred_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;

    // Convert the operands
    cond_type_repr = convert_type(context, llvm_cond_type);
    cond_repr = convert_value(context, llvm_cond);
    expr1_repr = convert_value(context, llvm_expr1);
    expr2_repr = convert_value(context, llvm_expr2);

    gcc_assert(cond_type_repr && cond_type_repr->get_tree());
    gcc_assert(cond_repr && cond_repr->get_tree());
    gcc_assert(expr1_repr && expr1_repr->get_tree());
    gcc_assert(expr2_repr && expr2_repr->get_tree());

    // Form the expression depending on whether it is a 
    // scalar or vector comparison.
    if (llvm_cond_type->isVectorTy())
      {
        gcc_assert(llvm_expr_type->isVectorTy());

        const llvm::VectorType* llvm_veccond_type =
          static_cast<const llvm::VectorType*>(llvm_cond_type);
        const unsigned num_elems = llvm_veccond_type->getNumElements();
        const llvm::VectorType* llvm_vecexpr_type =
          static_cast<const llvm::VectorType*>(llvm_expr_type);
        TypeRepr* base_type_repr = 0;
        tree base_type_tree = NULL_TREE;
        tree cond_tree = cond_repr->get_tree();
        tree expr1_tree = expr1_repr->get_tree();
        tree expr2_tree = expr2_repr->get_tree();
        Tree::VecMapper *mapper = 0;

        gcc_assert(Tree::is_vector_type(TREE_TYPE(expr1_tree)));
        gcc_assert(Tree::is_vector_type(TREE_TYPE(expr2_tree)));
        gcc_assert(Tree::vector_base_type(TREE_TYPE(expr2_tree)) 
                   == Tree::vector_base_type(TREE_TYPE(expr1_tree)));
        
        base_type_repr = convert_type(context, 
                                      llvm_vecexpr_type->getElementType());
        base_type_tree = base_type_repr->get_tree();
        gcc_assert(base_type_tree == TREE_TYPE(TREE_TYPE(expr1_tree)));

        mapper = new VecMapSelect(instr_loc, base_type_tree,
                                  cond_tree,
                                  expr1_tree,
                                  expr2_tree);

        instr_tree = Tree::make_mapped_vector(base_type_tree,
                                              num_elems,
                                              mapper);
        delete(mapper); mapper = 0;
      }
    else
      {
        TypeRepr* expr_type_repr = 
          convert_type(context, llvm_expr_type);
        gcc_assert(expr_type_repr && expr_type_repr->get_tree());

        // Scalar expressions.
        instr_opcode = COND_EXPR;
        pred_tree = cond_repr->get_tree();

        // Make the instruction
        instr_tree = fold_build3_loc(instr_loc,
                                     instr_opcode,
                                     expr_type_repr->get_tree(),
                                     pred_tree,
                                     expr1_repr->get_tree(),
                                     expr2_repr->get_tree());
      }

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }


  /* Convert a vaarg instruction. */
  InstrRepr* 
  Parser::convert_instr_vaarg(Context *context,
                              const llvm::Instruction* llvm_instr)
  {
    /*
     * va_arg expressions
     * --------
     * [| va_arg <argpack>, <arg_type> |]
     * -->
     * VA_ARG_EXPR [| arglist |]
     *
     */
    gcc_assert(llvm_instr);
    ObjContext* const curr_context = context->curr_context;
    location_t instr_loc = 
      convert_location(context, &(llvm_instr->getDebugLoc()));
    const llvm::VAArgInst* llvm_vaarg = 
      static_cast<const llvm::VAArgInst*>(llvm_instr);
    const llvm::Type* llvm_type = llvm_vaarg->getType();
    const llvm::Value* llvm_argpack = llvm_vaarg->getPointerOperand();
    TypeRepr* type_repr = 0;
    ValueRepr* argpack_repr = 0;
    InstrRepr* instr_repr = 0;
    tree instr_tree = NULL_TREE;

    curr_context->src_loc = instr_loc;

    gcc_assert(llvm_type);
    type_repr = convert_type(context, llvm_type);

    gcc_assert(llvm_argpack);
    argpack_repr = convert_value(context, llvm_argpack);

    gcc_assert(type_repr && type_repr->get_tree());
    gcc_assert(argpack_repr && argpack_repr->get_tree());

    // Form the VA_ARG expression.
    instr_tree = fold_build1_loc(instr_loc,
                                 VA_ARG_EXPR, type_repr->get_tree(),
                                 argpack_repr->get_tree());

    instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
    curr_context->register_value_repr(instr_repr);

    return instr_repr;
  }

  /* Toplevel for converting an LLVM instruction. This is the
     top-level function for converting instructions. It adds newly
     created representation objects to the tables, including those
     created by other instruction conversion functions.
  */
  InstrRepr*
  Parser::convert_instruction(Context *context,
                              const llvm::Value* llvm_value)
  { 
    gcc_assert(llvm_value);
    ObjContext* const curr_context = context->curr_context;
    const llvm::Instruction *llvm_instr =
      static_cast<const llvm::Instruction*>(llvm_value);
    const unsigned int llvm_opcode = llvm_instr->getOpcode();
    InstrRepr* found_instr_repr = 0;
    InstrRepr* instr_repr = 0;
    
    // Look up the LLVM instruction.
    found_instr_repr =
      static_cast<InstrRepr*>(context->lookup_value(llvm_instr));

    // Check whether it has already been converted, or is just
    // referenced.
    if (found_instr_repr && found_instr_repr->is_converted())
      return found_instr_repr;

    ////
    // Dispatch by instruction type.
    // Ordering follows declarations in llvm/Instruction.def
    switch (llvm_opcode)
      {
        // Terminator instructions
      case llvm::Instruction::Ret:
        instr_repr = convert_instr_ret(context, llvm_instr);
        break;
      case llvm::Instruction::Br:
        instr_repr = convert_instr_branch(context, llvm_instr);
        break;
      case llvm::Instruction::Switch:
        instr_repr = convert_instr_switch(context, llvm_instr);
        break;
      case llvm::Instruction::IndirectBr:
        instr_repr = convert_instr_indirectbr(context, llvm_instr);
        break;
      case llvm::Instruction::Invoke:
        instr_repr = convert_instr_invoke(context, llvm_instr);
        break;
      case llvm::Instruction::Unwind:
        instr_repr = convert_instr_unwind(context, llvm_instr);
        break;
      case llvm::Instruction::Unreachable:
        instr_repr = convert_instr_unwind(context, llvm_instr);
        break;

        // Binary operations
      case llvm::Instruction::Add:
      case llvm::Instruction::FAdd:
      case llvm::Instruction::Sub:
      case llvm::Instruction::FSub:
      case llvm::Instruction::Mul:
      case llvm::Instruction::FMul:
      case llvm::Instruction::UDiv:
      case llvm::Instruction::SDiv:
      case llvm::Instruction::FDiv:
      case llvm::Instruction::URem:
      case llvm::Instruction::SRem:
      case llvm::Instruction::FRem:
        instr_repr = convert_instr_binop_arith(context, llvm_instr);
        break;

        // Bitwise binary operations
      case llvm::Instruction::Shl:
      case llvm::Instruction::LShr:
      case llvm::Instruction::AShr:
      case llvm::Instruction::And:
      case llvm::Instruction::Or:
      case llvm::Instruction::Xor:
        instr_repr = convert_instr_binop_arith(context, llvm_instr);
        break;

        // Memory access and addressing operations
      case llvm::Instruction::Alloca:
        instr_repr = convert_instr_alloca(context, llvm_instr);
        break;
      case llvm::Instruction::Load:
        instr_repr = convert_instr_load(context, llvm_instr);
        break;
      case llvm::Instruction::Store:
        instr_repr = convert_instr_store(context, llvm_instr);
        break;
      case llvm::Instruction::GetElementPtr:
        instr_repr = convert_instr_getelementptr(context, llvm_instr);
        break;

        // Cast operations
      case llvm::Instruction::Trunc:
      case llvm::Instruction::ZExt:
      case llvm::Instruction::SExt:
      case llvm::Instruction::FPToUI:
      case llvm::Instruction::FPToSI:
      case llvm::Instruction::UIToFP:
      case llvm::Instruction::SIToFP:
      case llvm::Instruction::FPTrunc:
      case llvm::Instruction::FPExt:
      case llvm::Instruction::PtrToInt:
      case llvm::Instruction::IntToPtr:
      case llvm::Instruction::BitCast:
        instr_repr = convert_instr_cast(context, llvm_instr);
        break;

        // Other operations
      case llvm::Instruction::ICmp:
      case llvm::Instruction::FCmp:
        instr_repr = convert_instr_cmp(context, llvm_instr);
        break;
      case llvm::Instruction::PHI:
        instr_repr = convert_instr_phi(context, llvm_instr);
        break;
      case llvm::Instruction::Call:
        instr_repr = convert_instr_call(context, llvm_instr);
        break;
      case llvm::Instruction::Select:
        instr_repr = convert_instr_select(context, llvm_instr);
        break;

        // UserOp instructions are ignored.
      case llvm::Instruction::UserOp1:
      case llvm::Instruction::UserOp2:
        {
          location_t instr_loc = 
            convert_location(context, &(llvm_instr->getDebugLoc()));
          tree instr_tree = NULL_TREE;
          tree type_tree = void_type_node;

          if (llvm_instr->getType())
            {
              TypeRepr* type_repr = 
                convert_type(context, llvm_instr->getType());
              gcc_assert(type_repr && type_repr->get_tree());
              type_tree = type_repr->get_tree();
            }

          instr_tree = fold_build1_loc(instr_loc,
                                       NOP_EXPR, type_tree,
                                       NULL_TREE);
          instr_repr = new InstrRepr(llvm_instr, instr_tree, instr_tree);
          context->curr_context->register_value_repr(instr_repr);

          break;
        }
      case llvm::Instruction::VAArg:
        instr_repr = convert_instr_vaarg(context, llvm_instr);
        break;

        // Vector operations
      case llvm::Instruction::ExtractElement:
        instr_repr = convert_instr_extractelement(context, llvm_instr);
        break;
      case llvm::Instruction::InsertElement:
        instr_repr = convert_instr_insertelement(context, llvm_instr);
        break;
      case llvm::Instruction::ShuffleVector:
        instr_repr = convert_instr_shufflevector(context, llvm_instr);
        break;

        // Aggregate operations
      case llvm::Instruction::ExtractValue:
        instr_repr = convert_instr_extractvalue(context, llvm_instr);
        break;
      case llvm::Instruction::InsertValue:
        instr_repr = convert_instr_insertvalue(context, llvm_instr);
        break;

      default:
        break;
      }

    // Re-use existing representation object when possible.
    if (found_instr_repr)
      {
        found_instr_repr->copy(instr_repr);

        if (instr_repr)
          {
            curr_context->drop_value_repr(instr_repr);
            delete(instr_repr);
            instr_repr = 0;
          }

        instr_repr = found_instr_repr;
      }

    // Check for an unknown instruction.
    if (!instr_repr)
      {
        location_t instr_loc = 
          convert_location(context, &(llvm_instr->getDebugLoc()));
          
        error_at(instr_loc, "failed to convert LLVM instruction");
        gcc_unreachable();
      }

    return instr_repr;
  }

} // namespace llvmir
